package com.falser.user.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.falser.common.dto.MenuDTO;
import com.falser.common.dto.UserInfoDTO;
import com.falser.common.enums.DeleteEnum;
import com.falser.common.enums.PermissionTypeEnum;
import com.falser.common.enums.RedisKeyEnum;
import com.falser.common.enums.Status;
import com.falser.common.exception.BaseException;
import com.falser.user.dao.SysUserDao;
import com.falser.user.dto.LoginSuccessDTO;
import com.falser.user.dto.UserDetailDTO;
import com.falser.user.entity.SysPermission;
import com.falser.user.entity.SysRole;
import com.falser.user.entity.SysUser;
import com.falser.user.entity.SysUserRole;
import com.falser.user.enums.StatusEnum;
import com.falser.user.enums.UserTypeEnum;
import com.falser.user.service.SysRolePermissionService;
import com.falser.user.service.SysUserRoleService;
import com.falser.user.service.SysUserService;
import com.falser.user.vo.LoginVO;
import com.falser.user.vo.RegisterVO;
import com.falser.user.vo.SysUserInsertOrUpdateVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 用户信息表(SysUser)表服务实现类
 *
 * @author makejava
 * @since 2021-09-16 20:49:57
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> implements SysUserService {

	private final PasswordEncoder passwordEncoder;

	private final SysRolePermissionService sysRolePermissionService;

	private final SysUserRoleService sysUserRoleService;


	public SysUserServiceImpl(PasswordEncoder passwordEncoder,
	                          SysRolePermissionService sysRolePermissionService,
	                          SysUserRoleService sysUserRoleService) {
		this.passwordEncoder = passwordEncoder;
		this.sysRolePermissionService = sysRolePermissionService;
		this.sysUserRoleService = sysUserRoleService;
	}

	@Override
	public void register(RegisterVO vo) {
		SysUser one = getOne(new LambdaQueryWrapper<SysUser>()
				.eq(SysUser::getMobile, vo.getMobile()));
		if (Objects.nonNull(one)) {
			throw new BaseException("手机号已注册！");
		}

		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(vo, sysUser);
		sysUser.setCreateTime(LocalDateTime.now());
		sysUser.setPassword(passwordEncoder.encode(vo.getPassword()));
		sysUser.setDelFlag(DeleteEnum.UN_DELETED.getCode());
		sysUser.setPwdUpdateDate(LocalDateTime.now());
		sysUser.setUserType(UserTypeEnum.BLOG_USER);
		save(sysUser);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void addSystemUser(SysUserInsertOrUpdateVO vo) {
		SysUser sysUser = new SysUser();
		BeanUtils.copyProperties(vo, sysUser);
		sysUser.setSex(vo.getSex());
		sysUser.setUserType(vo.getUserType());
		sysUser.setStatus(vo.getStatus().name());
		checkAlreadyExist(sysUser);
		sysUser.setPassword(passwordEncoder.encode("123456"));
		// 修改
		if (Objects.nonNull(sysUser.getId())) {
			updateById(sysUser);
		} else {
			save(sysUser);
		}
	}

	@Override
	public LoginSuccessDTO login(LoginVO vo) {
		LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<SysUser>()
				.eq(SysUser::getLoginName, vo.getLoginName())
				.eq(SysUser::getStatus, StatusEnum.ENABLE.name());
		SysUser one = getOne(queryWrapper);
		if (Objects.nonNull(one) && passwordEncoder.matches(vo.getPassword(), one.getPassword())) {
			UUID uuid = UUID.randomUUID();
			StpUtil.login(uuid, new SaLoginModel().setTimeout(60 * 60 * 24 * 7));
			// 用户角色
			Long userId = one.getId();
			saveRolePermissionToRedis(userId);
			StpUtil.getSession().set("userinfo", getUserInfo(userId));
			one.setLoginIp(StpUtil.getLoginDevice());
			one.setLoginDate(LocalDateTime.now());
			updateById(one);
			return new LoginSuccessDTO()
					.setToken(StpUtil.getTokenValue())
					.setTokenKey(StpUtil.getTokenName());
		}
		throw new BaseException(Status.USERNAME_PASSWORD_ERROR);
	}

	private UserInfoDTO getUserInfo(Long userId) {
		SysUser sysUser = getById(userId);
		UserInfoDTO target = new UserInfoDTO();
		target.setName(sysUser.getUserName());
		target.setAvatar(sysUser.getAvatar());
		List<SysRole> sysRoleList = sysUserRoleService.getRoleListByUserId(userId);
		List<Long> idList = sysRoleList.stream().map(SysRole::getId).collect(Collectors.toList());
		List<String> roles = sysRoleList.stream().map(role -> role.getRoleKey().name()).collect(Collectors.toList());
		target.setRoles(roles);
		List<SysPermission> permissionListByRoleIds = sysRolePermissionService.getPermissionListByRoleIds(idList);
		List<MenuDTO> menus = permissionListByRoleIds
				.stream()
				.filter(sysPermission -> Objects.equals(PermissionTypeEnum.MENU.name(), sysPermission.getPermissionType().toString()))
				.map(sysPermission -> {
					MenuDTO menuDTO = new MenuDTO();
					BeanUtils.copyProperties(sysPermission, menuDTO);
					return menuDTO;
				})
				.collect(Collectors.toList());
		target.setMenus(menus);
		return target;
	}

	/**
	 * 保存角色和权限到redis
	 *
	 * @param userId 用户id
	 */
	private void saveRolePermissionToRedis(Long userId) {
		List<SysRole> roleList = sysUserRoleService.getRoleListByUserId(userId);
		String tokenValue = StpUtil.getTokenValue();
		StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.USER_ID_TOKEN_REF_KEY.getKey() + tokenValue, userId);
		// 用户权限
		if (!CollectionUtils.isEmpty(roleList)) {
			StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.LOGIN_ROLES_KEY.getKey() + tokenValue, roleList
					.stream()
					.map(SysRole::getRoleKey)
					.collect(Collectors.toList()));

			List<Long> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toList());

			List<SysPermission> permissionListByRoleIds = sysRolePermissionService.getPermissionListByRoleIds(roleIds);

			if (!CollectionUtils.isEmpty(permissionListByRoleIds)) {
				StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.LOGIN_PERMISSIONS_KEY.getKey() + tokenValue, permissionListByRoleIds
						.stream()
						.map(sysPermission -> sysPermission.getMethod() + "#" + sysPermission.getUrl())
						.collect(Collectors.toList()));
			}

		}
	}

	@Override
	public void logout() {
		StpUtil.logout();
	}

	@Override
	public UserInfoDTO getUserInfo() {
		return (UserInfoDTO) StpUtil.getSession().get("userinfo");
	}

	@Override
	public UserDetailDTO getUserDetailById(Long id) {
		return baseMapper.getUserDetailById(id);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void deleteUserById(Long id) {
		removeById(id);
		sysUserRoleService.remove(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, id));
	}

	/**
	 * 检查已经存在
	 *
	 * @param sysUser 系统用户
	 */
	private void checkAlreadyExist(SysUser sysUser) {
		SysUser user = getOne(new LambdaQueryWrapper<SysUser>()
				.eq(SysUser::getMobile, sysUser.getMobile()));
		if (Objects.nonNull(user)) {
			throw new BaseException(Status.PHONE_EXIST_EXCEPTION);
		}

		SysUser user1 = getOne(new LambdaQueryWrapper<SysUser>()
				.eq(SysUser::getLoginName, sysUser.getLoginName()));
		if (Objects.nonNull(user1)) {
			throw new BaseException(Status.USER_EXIST_EXCEPTION);
		}

	}
}

